package org.moonlight.jvm.instructions.base;

import lombok.Getter;

/**
 * 字节码读取器
 *
 * @author Moonlight
 * @createTime 2023/8/16 13:41
 **/
public class BytecodeReader {

    /** 存储字节码数据 **/
    private byte[] code;
    /** 记录读取到了哪个字节 **/
    @Getter
    private int pc;

    /**
     * 重置字节码读取器
     * @param code 字节码存储数组
     * @param pc 当前读取到了哪个字节
     * @createTime 13:43 2023/8/16
     * @author moonlight
     **/
    public void reset(byte[] code, int pc) {
        this.code = code;
        this.pc = pc;
    }

    /**
     * 读取操作码(字节码指令)，操作码(字节码指令)应该是一字节的无符号数字
     *
     * @return int 操作码(字节码指令)
     * @createTime 11:46 2023/9/28
     * @author moonlight
     **/
    public int readOpCode() {
        byte b = this.code[this.pc];
        this.pc++;
        return Byte.toUnsignedInt(b);
    }

    /**
     * 读取一字节，转为 int 返回
     *
     * @return int
     * @createTime 13:59 2023/8/16
     * @author moonlight
     **/
    public int readInt8() {
        byte b = this.code[this.pc];
        this.pc++;
        return b;
    }

    /**
     * 读取两字节，转为 int 返回
     * @return int
     * @createTime 13:59 2023/8/16
     * @author moonlight
     **/
    public int readInt16() {
        // 这里需要 无符号数字进行运算，不然遇见边界值会出现解析错误
        int high = readOpCode();
        int low = readOpCode();
        // 这里只需要2字节，而这样子经过运算后可能超过了两字节，所以转为short抹去溢出的，再自动转型为int返回
        return (short)((high << 8) | low);
    }

    /**
     * 读取四字节，转为 int 返回
     * @return int
     * @createTime 13:59 2023/8/16
     * @author moonlight
     **/
    public int readInt32() {
        int high = readInt16();
        int low = readInt16();
        return (high << 16) | low;
    }

    /**
     * 读取多个四字节，转为 int数组 返回
     * @return int[]
     * @createTime 13:59 2023/8/16
     * @author moonlight
     **/
    public int[] readInt32s(int n) {
        int[] arr = new int[n];
        for (int i = 0; i < n; i++) {
            arr[i] = readInt32();
        }
        return arr;
    }

    /**
     * 跳过填充对齐的字节
     * @createTime 14:02 2023/8/16
     * @author moonlight
     **/
    public void skipPadding() {
        while (this.pc % 4 != 0) {
            this.readInt8();
        }
    }
}